package com.starsoft.frame.base.job;

import java.util.List;

import javax.annotation.PostConstruct;

import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.starsoft.frame.base.bean.BaseJob;
import com.starsoft.frame.base.service.JobService;
import com.starsoft.frame.base.service.PreferenceService;
import com.starsoft.frame.util.StringUtil;

@Component
public class JobRegister {

	private static Logger logger = LoggerFactory.getLogger(JobRegister.class);
	
	private final static String JOB_GROUP_NAME = "frame_job";

	private final static String TRIGGER_GROUP_NAME = "frame_trigger";
	
	private final static String JOB_GROUP_NAME_ONE = "frame_job_onetime";
	
	@Autowired
	Scheduler scheduler;
	
	@Autowired
	JobService jobService;
	
	@Autowired
	PreferenceService ps;
	
	@PostConstruct
	public void register() throws SchedulerException {
		String env = ps.getPreference("env");
		if (!"prod".equals(env)) {
			logger.info("Not prod env, Don't regist Job");
			return;
		}
		logger.info("Start to regist Job");
		List<BaseJob> jobs = jobService.getAll();
		for (BaseJob job : jobs) {
			String jobName = job.getJobName();
			try {
				if (checkRunnable(job)) {
					updateJob(job);
					logger.info("Start job:" + jobName);
				}
			} catch (Exception e) {
				logger.error("Start job failed:" + jobName, e);
			}
		}
	}
	
	private boolean checkRunnable(BaseJob job){
		if (job.getIsOpen()==null || job.getIsOpen()!=true) {
			return false;
		}
		if (StringUtil.isEmpty(job.getCron()) && StringUtil.isEmpty(job.getIntervalMinute())) {
			return false;
		}
		return true;
	}
	

	@SuppressWarnings("unchecked")
	public void updateJob(BaseJob job) throws SchedulerException, ClassNotFoundException{	
		boolean runnable = checkRunnable(job);
		JobDetail jobDetail = scheduler.getJobDetail(getJobKey(job));

		if(jobDetail==null && runnable==false){
			
		} else if(jobDetail!=null && runnable==false){
			deleteJob(job);
		} else if(jobDetail==null && runnable!=false){
			addJob(job);
		} else {
			Class<Job> jobClass = (Class<Job>) Class.forName(job.getJobClass());
			if(jobDetail.getJobClass().equals(jobClass)){
				updateTrigger(job);
			}else{
				deleteJob(job);
				addJob(job);
			}
		}
	}
	
	public void deleteJob(BaseJob job) throws SchedulerException{
		JobKey jobKey = getJobKey(job);
		TriggerKey triggerKey = getTriggerKey(job);
		scheduler.pauseTrigger(triggerKey);
		scheduler.unscheduleJob(triggerKey);
		scheduler.deleteJob(jobKey);
	}
	
	@SuppressWarnings("unchecked")
	public void addJob(BaseJob job) throws SchedulerException {
		JobKey jobKey = getJobKey(job);
		TriggerKey triggerKey = getTriggerKey(job);
		
		Class<Job> jobClass = null;
		try {
			jobClass = (Class<Job>) Class.forName(job.getJobClass());
		} catch (ClassNotFoundException e) {
			logger.error("Job {} Class not found " + e.getMessage(), job.getJobName());
			return;
		}		
		JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).build();
		jobDetail.getJobDataMap().put("baseJob", job);
		
		Trigger trigger = null;
		if(!StringUtil.isEmpty(job.getCron())){
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
				.withSchedule(CronScheduleBuilder.cronSchedule(job.getCron())).startNow().build();
		} else {
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
					.withSchedule(SimpleScheduleBuilder.repeatMinutelyForever(job.getIntervalMinute())).startNow()
					.build();
		}
		scheduler.scheduleJob(jobDetail, trigger);
	}
	
	public void updateTrigger(BaseJob job) throws SchedulerException{
		TriggerKey triggerKey = getTriggerKey(job);
		Trigger trigger = null;
		if(!StringUtil.isEmpty(job.getCron())){
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
				.withSchedule(CronScheduleBuilder.cronSchedule(job.getCron())).startNow().build();
		} else {
			trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
					.withSchedule(SimpleScheduleBuilder.repeatMinutelyForever(job.getIntervalMinute())).startNow()
					.build();
		}
		scheduler.rescheduleJob(triggerKey, trigger);
	}
	
	@SuppressWarnings("unchecked")
	public void triggerOneTimeJob(BaseJob job) throws SchedulerException{
		JobKey jobKey = new JobKey(job.getJobId()+"", JOB_GROUP_NAME_ONE);
		JobDetail jobDetail = scheduler.getJobDetail(jobKey);
		if(jobDetail==null){
			Class<Job> jobClass = null;
			try {
				jobClass = (Class<Job>) Class.forName(job.getJobClass());
			} catch (ClassNotFoundException e) {
				logger.error("Job {} Class not found " + e.getMessage(), job.getJobName());
				throw new RuntimeException("Job Class not found " + job.getJobClass());
			}
			jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobKey).storeDurably().build();
			jobDetail.getJobDataMap().put("baseJob", job);
			scheduler.addJob(jobDetail, true);
		}
		scheduler.triggerJob(jobKey);
	}
	
	private JobKey getJobKey(BaseJob job){
		return new JobKey(job.getJobId()+"", JOB_GROUP_NAME);
	}
	
	private TriggerKey getTriggerKey(BaseJob job){
		return new TriggerKey(job.getJobId()+"", TRIGGER_GROUP_NAME);
	}
}
